#ifndef __M_EXG_H__
#define __M_EXG_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
//#include "../mqcommon/mq_proto.pb.h"
#include <google/protobuf/map.h>
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace castlemq
{   
    //1.定义交换机类
    struct Exchange
    {
        using ptr=std::shared_ptr<Exchange>;
        Exchange(){}
        Exchange(const std::string& ename,
        ExchangeType etype,
        bool edurable,
        bool eauto_delete,
        const google::protobuf::Map<std::string, std::string>& eargs):
        name(ename),type(etype),durable(edurable),auto_delete(eauto_delete),args(eargs)
        {}

        //args存储键值对，存储数据库时，会组织一个格式字符串进行存储，key=value&key=value...
        void setArgs(const std::string& str_args)//解析str_args存储到args里
        {
            std::vector<std::string> sub_args;
            StrHelper::split(str_args,"&",sub_args);
            for(auto& x:sub_args)
            {
                size_t pos = x.find("=");
                std::string key=x.substr(0,pos);
                std::string val=x.substr(pos+1);
                args[key]=val;
            }
        }

        //序列化args后返回一个字符串
        std::string getArgs()
        {
            std::string ret;
            for(auto it=args.begin();it!=args.end();it++)
            {
                ret+=it->first+"="+it->second+"&";
            }
            return ret;
        }

        std::string name;//名称
        ExchangeType type;//类型
        bool durable;//持久化标志
        bool auto_delete;//自动删除标志
        google::protobuf::Map<std::string, std::string> args;//其他参数
    };

    //2.定义交换机持久化管理类--存储在sqlite数据库中
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string& dbfile):_sql_helper(dbfile)
        {
            std::string path = fileHelper::parentDictory(dbfile);
            fileHelper::createDictory(path);
            assert(_sql_helper.open());
            creatTable();
        }
        void creatTable()
        {
            #define CREAT_TABLE "create table if not exists exchange_table(name varchar(32) primary key,\
            type int,durable int,auto_delete int,args varchar(128));"
            char sql_str[4096] = {0};
            sprintf(sql_str,CREAT_TABLE);
            bool ret=_sql_helper.exec(sql_str,nullptr,nullptr);
            if(!ret)
            {
                DLOG("创建交换机表失败！");
                abort();//结束进程
            }
        }
        void removeTable()
        {
            #define DROP_TABLE "drop table if exists exchange_table;"
            char sql_str[4096] = {0};
            sprintf(sql_str,DROP_TABLE);
            bool ret=_sql_helper.exec(sql_str,nullptr,nullptr);
            if(!ret)
            {
                DLOG("删除交换机表失败！");
            }
        }
        void insert(Exchange::ptr& exchange)
        {
            #define INSERT_TABLE "insert into exchange_table values('%s',%d,%d,%d,'%s');"
            std::string args_str=exchange->getArgs();
            char sql_str[4096] = {0};
            sprintf(sql_str,INSERT_TABLE,(exchange->name).c_str(),exchange->type,exchange->durable,exchange->auto_delete,args_str.c_str());
            _sql_helper.exec(sql_str,nullptr,nullptr);
        }
        void remove(const std::string& name)
        {
            #define REMOVE_TABLE "delete from exchange_table where name='%s';"
            char sql_str[4096] = {0};
            sprintf(sql_str,REMOVE_TABLE,name.c_str());
            _sql_helper.exec(sql_str,nullptr,nullptr);
        }
        
        using ExchangeMap = std::unordered_map<std::string,Exchange::ptr>;
        ExchangeMap recovery()
        {
            ExchangeMap result;
            std::string ss="select name,type,durable,auto_delete,args from exchange_table;";
            _sql_helper.exec(ss.c_str(),SqliteCallback,&result);
            return result;
        } 
    private:
        static int SqliteCallback(void* arg,int numcol,char** row,char** fields)
        {
            ExchangeMap* result = (ExchangeMap*)arg;
            auto exp=std::make_shared<Exchange>();
            exp->name=row[0];
            exp->type=(castlemq::ExchangeType)std::stoi(row[1]);
            exp->durable=(bool)std::stoi(row[2]);
            exp->auto_delete=(bool)std::stoi(row[3]);
            if(row[4]) exp->setArgs(row[4]);
            result->insert(std::make_pair(exp->name,exp));
            return 0;
        }
    private:
        SqliteHelper _sql_helper;
    };

    //3.定义交换机数据内存管理类
    class ExchangeManager
    {
        public:
        using ptr=std::shared_ptr<ExchangeManager>;
            ExchangeManager(const std::string& dbfile):_mapper(dbfile)
            {
                _exchanges=_mapper.recovery();
            }
            bool declareExchange(const std::string& name,
                    ExchangeType type,bool durable,bool auto_delete,
                    const google::protobuf::Map<std::string, std::string>& args)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_exchanges.find(name);
                if(it!=_exchanges.end())
                {
                    return true;//交换机存在就返回
                }
                auto exp=std::make_shared<Exchange>();
                exp->name=name;
                exp->type=type;
                exp->durable=durable;
                exp->auto_delete=auto_delete;
                exp->args=args;
                
                if(durable)
                {
                    _mapper.insert(exp);
                }
                _exchanges.insert(std::make_pair(name,exp));
                return true;
            }
            void deleteExchange(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_exchanges.find(name);
                if(it==_exchanges.end())
                    return;//交换机不存在就返回
                if(it->second->durable) _mapper.remove(name);
                _exchanges.erase(name);
            } 
            Exchange::ptr selectExchange(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_exchanges.find(name);
                if(it==_exchanges.end())
                    return Exchange::ptr();
                return it->second;
            }
            bool exsits(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_exchanges.find(name);
                if(it==_exchanges.end())
                    return false;
                return true;
            }
            size_t size()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _exchanges.size();
            }
            void clear()
            {
                _mapper.removeTable();
                _exchanges.clear();
            }

        private:
            std::mutex _mutex;
            ExchangeMapper _mapper;
            std::unordered_map<std::string,Exchange::ptr> _exchanges;
    };
}
#endif